package cn.chiship.sdk.third.service;

import cn.chiship.sdk.cache.service.RedisService;
import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.exception.ExceptionUtil;

import cn.chiship.sdk.core.exception.custom.BusinessException;
import cn.chiship.sdk.core.util.*;
import cn.chiship.sdk.core.util.http.HttpUtil;
import cn.chiship.sdk.third.core.common.ThirdConstants;
import cn.chiship.sdk.third.core.model.WeiXinConfigModel;
import cn.chiship.sdk.third.core.wx.util.WxBizDataCrypt;
import cn.chiship.sdk.third.properties.ChishipWxMiniProgramProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信小程序服务 URL:<a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/">文档</a>
 *
 * @author lijian
 */
@Component
public class WxMpService {

    /**
     * Weixin Server服务主机
     */
    public static final String API_WEI_XIN_SERVER_HOST = "https://api.weixin.qq.com/";

    private static final Logger LOGGER = LoggerFactory.getLogger(WxMpService.class);

    private static final String ACCESS_TOKEN = "access_token";

    private WeiXinConfigModel weiXinConfigModel;

    private String accessToken = null;

    @Resource
    ChishipWxMiniProgramProperties chishipWxMiniProgramProperties;

    @Resource
    RedisService redisService;

    public WxMpService config() {
        this.weiXinConfigModel = new WeiXinConfigModel(chishipWxMiniProgramProperties.getAppKey(),
                chishipWxMiniProgramProperties.getAppSecret());
        return this;
    }

    public WxMpService config(WeiXinConfigModel weiXinConfigModel) {
        if (ObjectUtils.isEmpty(weiXinConfigModel)) {
            LOGGER.info("微信小程序实例配置为空，将自动加载默认配置信息");
            return config();
        }
        this.weiXinConfigModel = weiXinConfigModel;
        return this;
    }

    public WxMpService token() {
        BaseResult baseResult = getToken();
        if (!baseResult.isSuccess()) {
            throw new BusinessException(baseResult.getData() + "-" + baseResult.getMessage());
        }
        this.accessToken = baseResult.getData().toString();
        return this;
    }

    /**
     * 获取接口调用凭据
     * URL:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
     *
     * @return BaseResult
     */
    private BaseResult getToken() {

        try {
            String getTokenUrl = API_WEI_XIN_SERVER_HOST + "cgi-bin/token";

            String key = ThirdConstants.REDIS_WEIXIN_MINI_PROGRAM_ACCESS_TOKEN + ":"
                    + this.weiXinConfigModel.getAppKey();
            String token = StringUtil.getString(redisService.get(key), null);
            if (!StringUtil.isNullOrEmpty(token)) {
                return BaseResult.ok(token);
            }
            Map<String, Object> query = new HashMap<>(7);
            query.put("grant_type", "client_credential");
            query.put("appid", this.weiXinConfigModel.getAppKey());
            query.put("secret", this.weiXinConfigModel.getAppSecret());
            BaseResult baseResult = HttpUtil.getInstance().doGet(getTokenUrl, query);
            baseResult = analysisHttpResponse(baseResult);
            if (!baseResult.isSuccess()) {
                return baseResult;
            }
            JSONObject dataJson = (JSONObject) baseResult.getData();
            token = dataJson.getString(ACCESS_TOKEN);
            long expiresIn = Long.parseLong(dataJson.getString("expires_in"));
            baseResult.setData(token);
            // 提前5分钟过期
            redisService.set(key, token, expiresIn - 5 * 60);
            return baseResult;
        } catch (Exception e) {
            return ExceptionUtil.formatException(e);
        }
    }

    /**
     * 通过code换取网页授权access_token
     * URL:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
     *
     * @param code 登录时code 通过wx.login获取
     * @return BaseResult
     */
    public BaseResult code2Session(String code) {
        try {
            String code2SessionUrl = API_WEI_XIN_SERVER_HOST + "sns/jscode2session";
            Map<String, Object> query = new HashMap<>(7);
            query.put("grant_type", "authorization_code");
            query.put("appid", weiXinConfigModel.getAppKey());
            query.put("secret", weiXinConfigModel.getAppSecret());
            query.put("js_code", code);
            BaseResult baseResult = HttpUtil.getInstance().doGet(code2SessionUrl, query);
            return analysisHttpResponse(baseResult);
        } catch (Exception e) {
            return ExceptionUtil.formatException(e);
        }
    }

    /**
     * 解密开放数据
     *
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据 通过 open-type="getPhoneNumber"
     *                      bindgetphonenumber="***"获取
     * @param iv            加密初始向量 通过 open-type="getPhoneNumber" bindgetphonenumber="***"获取
     * @param sessionKey    通过code2Session接口换取的sessionKey
     * @return BaseResult
     */
    public BaseResult decryptingOpenData(String encryptedData, String iv, String sessionKey) {
        WxBizDataCrypt wxBizDataCrypt = new WxBizDataCrypt(encryptedData, sessionKey, iv);
        return wxBizDataCrypt.decryptData();
    }

    /**
     * 获取手机号
     * URL:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html
     *
     * @param code 动态令牌 通过 open-type="getPhoneNumber" bindgetphonenumber="***"获取
     * @return BaseResult
     */
    public BaseResult getPhoneNumber(String code) {

        try {
            String getUserPhoneUrl = API_WEI_XIN_SERVER_HOST + "wxa/business/getuserphonenumber";
            Map<String, Object> query = new HashMap<>(7);
            query.put(ACCESS_TOKEN, getAccessToken());
            JSONObject body = new JSONObject();
            body.put("code", code);
            BaseResult baseResult = HttpUtil.getInstance().doPost(getUserPhoneUrl, query, body);
            baseResult = analysisHttpResponse(baseResult);
            if (!baseResult.isSuccess()) {
                return baseResult;
            }
            JSONObject json = (JSONObject) baseResult.getData();
            JSONObject phoneInfo = json.getJSONObject("phone_info");
            phoneInfo.remove("watermark");
            Map<String, String> map = new HashMap<>(7);
            map.put("phoneNumber", phoneInfo.getString("purePhoneNumber"));
            map.put("countryCode", phoneInfo.getString("countryCode"));
            baseResult.setData(map);
            return baseResult;
        } catch (Exception e) {
            return ExceptionUtil.formatException(e);
        }
    }

    /**
     * 生成小程序二维码
     * URL:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getUnlimitedQRCode.html
     *
     * @param scene 场景
     * @param page  默认页面
     * @return BaseResult
     */
    public BaseResult getQrCodeUnLimit(String scene, String page) {
        return getQrCodeUnLimit(scene, 430, page);
    }

    /**
     * 生成小程序二维码
     *
     * @param scene 场景
     * @param width 宽度 默认 430
     * @param page  默认页面
     * @return BaseResult.ok(byte[])    BaseResult.error(****)
     */
    public BaseResult getQrCodeUnLimit(String scene, int width, String page) {
        try {
            String qrCodeUnLimitUrl = API_WEI_XIN_SERVER_HOST + "wxa/getwxacodeunlimit";

            int defaultWidth = 430;
            int minWidth = 280;
            int maxWidth = 1280;
            int sceneLength = 32;

            if (StringUtil.isNullOrEmpty(scene)) {
                return BaseResult.error("场景值不能为空！");
            }
            if (scene.length() > sceneLength) {
                return BaseResult.error("场景值不能大于32个字符！");
            }
            if (StringUtil.isNull(width)) {
                width = defaultWidth;
            }
            if (width > maxWidth || width < minWidth) {
                return BaseResult.error("二维码宽度必须280~1280！");
            }

            Map<String, Object> query = new HashMap<>(7);
            query.put(ACCESS_TOKEN, getAccessToken());
            Map<String, Object> body = new HashMap<>(7);
            body.put("scene", scene);
            body.put("width", width);
            if (!StringUtil.isNullOrEmpty(page)) {
                body.put("page", page);
            }
            BaseResult baseResult = HttpUtil.getInstance().doPost(qrCodeUnLimitUrl, query, body);
            if (!baseResult.isSuccess()) {
                return baseResult;
            }
            try {
                baseResult = analysisHttpResponse(baseResult);
            } catch (Exception ignored) {
            }
            return baseResult;
        } catch (Exception e) {
            e.printStackTrace();
            return ExceptionUtil.formatException(e);
        }
    }

    public String getAccessToken() {
        if (StringUtil.isNullOrEmpty(accessToken)) {
            throw new BusinessException("token为空！请链式调用如下方法：config().token()获得Token");
        }
        return accessToken;
    }

    public WeiXinConfigModel getWeiXinConfigModel() {
        return weiXinConfigModel;
    }

    private BaseResult analysisHttpResponse(BaseResult baseResult) {
        Integer successStatus = 0;
        String errCode = "errcode";
        String errMsg = "errmsg";
        if (!baseResult.isSuccess()) {
            return baseResult;
        }
        JSONObject json = JSON.parseObject(StringUtil.getString(baseResult.getData()));
        if (json.containsKey(errCode)) {
            if (successStatus.equals(json.getInteger(errCode))) {
                return BaseResult.ok(json);
            } else {
                return BaseResult.error("【" + json.getInteger(errCode) + "】：" + json.getString(errMsg));
            }
        } else {
            return BaseResult.ok(json);
        }

    }

}
